package com.isotronica.neveras.logica;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.sql.Date;
import java.sql.Time;
import java.util.GregorianCalendar;


public class DataDecoder extends Thread {

	

	private Socket socket;
	private InputStream inputStream;
	private OutputStream outputStream;
	private Thread t;
		
	private Controlador miControlador;
	
	
	private int estado_trama = 1;

	private int state = 1;
	private static byte[] bytes = new byte[12];
	private static byte[] bytesId = new byte[3];
	private static float[] myarray = new float[6];
	private static int cont = 0;
	private int conTimeOut = 0;
	private int sensor = 0;

	private int ano = 0;
	private int mes = 0;
	private int dia = 0;
	private int hora = 0;
	private int minuto = 0;
	private byte energia = 0;
	private Thread hilo;
	private ManejoDatos manejoDatos;
	private boolean huboDatos = false;
	

	public DataDecoder(Socket socket, Controlador miControlador) {
		
		this.socket = socket;
		this.miControlador = miControlador;
		manejoDatos = new ManejoDatos(this);
		try {
			inputStream = this.socket.getInputStream();
			outputStream = this.socket.getOutputStream();

		} catch (IOException e) {
			e.printStackTrace();
		}
		t = new Thread(this);
		t.setPriority(NORM_PRIORITY);
		t.start();
	}

	

	public void run() {
		System.out
				.println(socket.getInetAddress().toString()
						+ "  Hilo Iniciado\n========================================\n ");
		miControlador.appendText(socket.getInetAddress().toString()
						+ "  Hilo Iniciado");
		
		InputStream entrada = null;
		OutputStream salida = null;
		int tipor = 0;
		String datosTemp = "";
		try {
			entrada = socket.getInputStream();
			salida = socket.getOutputStream();
		} catch (java.io.IOException e) {
			
			System.out
			.println("Error Abriendo Conexion ");
			miControlador.appendText("Error Abriendo Conexion ");
		}
		conTimeOut =0;
		while (conTimeOut < 60) {
			try {
				sleep(1000);
			} catch (InterruptedException e) {
			}
			conTimeOut++;
			try {
				while (entrada.available() > 0) {
					
					int dato = entrada.read();
					huboDatos = true;
									
					datosTemp = datosTemp + dato;
					switch (estado_trama ) {
					case 0:
						break; // estado de espera
					case 1:
						if (dato == 0x7E) {
							estado_trama = 2;
						} else {
							estado_trama = 1;
						}
						break;
					case 2:
						if (dato == 0x7E) {
							estado_trama = 3;
						} else {
							estado_trama = 1;
						}
						break;
					case 3:
						tipor = dato;
						if (tipor == 8) {  /// confirmacion de recibo de informacion
							estado_trama = 21;
						} else {
							estado_trama = 4;
						}
						break;
					case 4:
						ano = dato;
						estado_trama = 5;
						break;
					case 5:
						mes = dato;
						estado_trama = 6;
						break;
					case 6:
						dia = dato;
						estado_trama = 7;
						break;

					case 7:
						hora = dato;
						estado_trama = 8;
						break;
					case 8:
						minuto = dato;
						estado_trama = 9;
						break;
					case 9:
						bytes[0] = (byte) dato;
						estado_trama = 10;
						break;
					case 10:
						bytes[1] = (byte) dato;
						estado_trama = 11;
						break;
					case 11:
						bytes[2] = (byte) dato;
						estado_trama = 12;
						break;

					case 12:
						bytes[3] = (byte) dato;
						estado_trama = 13;
						break;
					case 13:
						bytes[4] = (byte) dato;
						estado_trama = 14;
						break;
					case 14:
						bytes[5] = (byte) dato;
						estado_trama = 15;
						break;
					case 15:
						bytes[6] = (byte) dato;
						estado_trama = 16;
						break;
					case 16:
						bytes[7] = (byte) dato;
						estado_trama = 17;
						break;

					// / Nuevo ID
					case 17:
						bytesId[0] = (byte) dato;
						estado_trama = 18; // Para compatibilidad se camba a 19
						bytesId[1] = 0; // // borrar cuando fulll
						break;
					case 18:
						bytesId[1] = (byte) dato;
						estado_trama = 19;
						break;
					case 19:
						energia = (byte) dato;
						estado_trama = 20;
						break;
					case 20:
						estado_trama = 1;
						// realiza la conversión de tipos
						cont = 0;
						if (dato == 0x1A) {
							for (int start = 0; start < 8; start = start + 4) {
								myarray[cont] = arr2float(bytes, start);
								cont++;
							}
							

							if (ano!= 0 && mes != 0) {
								GregorianCalendar fechat = new GregorianCalendar(
										2000 + ano, mes - 1, dia, hora, minuto);
								Time horat = new Time(fechat.getTimeInMillis());
								Date fecha = new Date(fechat.getTimeInMillis());

								// Time horat = new
								// Time(System.currentTimeMillis());
								// Date fecha = new
								// Date(System.currentTimeMillis());
								byte estado = energia == 0 ? (byte) 1 : 0;
								manejoDatos.escribirDatoDb(horat, fecha, myarray[0],
										myarray[1], estado, bytesId, salida,
										tipor);
								sensor = arr2int(bytesId, 0);
								System.out.println("Datos Recibidos de "+sensor+ " hora "+ horat.toString() +" fecha "+fecha.toString()+" Tem "+ myarray[1]+" hum "+ myarray[0]+ " Ener "+ energia+" \n");
								miControlador.appendText("Datos Recibidos de "+sensor+ " hora "+ horat.toString() +" fecha "+fecha.toString()+" Tem "+ myarray[1]+" hum "+ myarray[0]+ " Ener "+ energia);
								
							}

						} else {
							System.out.println("Error: Datos Corruptos del modulo "+ sensor + " ["+datosTemp+"]\r\n");
							miControlador.appendText("Error: Datos Corruptos del modulo "+ sensor + " ["+datosTemp+"]\r\n");
						}

						break;

					// ////////////////////////////////////// Llegada del ACK ////////////////////////////////////
					case 21:
						bytesId[0] = (byte) dato;
						estado_trama = 22; 
						bytesId[1] = 0; 
						break;
					case 22:
						bytesId[1] = (byte) dato;
						estado_trama = 23;
						break;
					case 23:
						estado_trama = 1;				
						
						cont = 0;
						if (dato == 0x1A) {
							manejoDatos.limpiarBandera(bytesId, salida);

						}
						break;

					}

				}
			} catch (Exception e) {				
			
			}
		}
		// System.out.println(" ");
		try {
			sleep(2000);
		} catch (InterruptedException e) {
		}
		try {
			socket.close();
			if(huboDatos){
				System.out.println("Conexion Cerrada con " + socket.getInetAddress()+ "\r\n Tiempo " + conTimeOut);
				miControlador.appendText("Conexion Cerrada con " + socket.getInetAddress()+ "\r\n Tiempo " + conTimeOut);
			}else{
				System.out.println("Conexion Cerrada con " + socket.getInetAddress()+ " Sin datos Recibidos\r\n");
				miControlador.appendText("Conexion Cerrada con " + socket.getInetAddress() + " Sin datos Recibidos");
			}
			
		
		} catch (IOException e) {

				System.out.println("IOException:");
			
		}		
	}



	
	public static int arr2int(byte[] arr, int start) {
		int i = 0;
		int len = 2;
		int cnt = 0;
		byte[] tmp = new byte[len];
		for (i = start; i < (start + len); i++) {
			tmp[cnt] = arr[i];
			cnt++;
		}
		int accum = 0;
		i = 0;
		for (int shiftBy = 0; shiftBy < 16; shiftBy += 8) {
			accum |= ((long) (tmp[i] & 0xff)) << shiftBy;
			i++;
		}
		return accum;
	}

	

	public void setConTimeOut(int conTimeOut) {
		this.conTimeOut = conTimeOut;
	}
	
	public Controlador getMiControlador() {
		return this.miControlador;
	}

	public void sendDato(String comando) {
		try {
			byte[] prueba = comando.getBytes();

			outputStream.write(prueba);
			outputStream.flush();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static float arr2float(byte[] arr, int start) {
		int i = 0;
		int len = 4;
		int cnt = 0;
		byte[] tmp = new byte[len];
		for (i = start; i < (start + len); i++) {
			tmp[cnt] = arr[i];
			cnt++;
		}
		int accum = 0;
		i = 0;
		for (int shiftBy = 0; shiftBy < 32; shiftBy += 8) {
			accum |= ((long) (tmp[i] & 0xff)) << shiftBy;
			i++;
		}
		return Float.intBitsToFloat(accum);
	}
}
